Jdbc 테스트에서 NoSuchBeanDefinitionException 해결

What is Problem

RacingCarDAOTest에서 필드로 carDAO를 갖고, 구현체 RacingCarDAO를 필드 주입을 통해 주입하려는 상황

RacingCarDAO

@Repository  
public class RacingCarDAO implements CarDAO {  
    private final JdbcTemplate jdbcTemplate;  
...
}

이렇게 @Repository 어노테이션을 붙여놓음으로써 빈 등록을 하였다.

그리고 RacingCarDAOTest에서는 다음과 같이 사용하려고 한다.

@JdbcTest  
@Sql(scripts = {"classpath:data.sql"})  
class RacingCarDAOTest {  
    @Autowired  
    private JdbcTemplate jdbcTemplate;  
    @Autowired  
    private RacingCarDAO racingCarDAO;

하지만 아래와 같은 에러가 발생한다.

Error creating bean with name 'racingcar.database.RacingCarDAOTest': Unsatisfied dependency expressed through field 'racingCarDAO'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'racingcar.car.repository.RacingCarDAO' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

RacingCarDAO가 빈 등록이 되어있지 않는다는 상황

Why it happnes

@JdbcTest는 모든 컴포넌트를 스캔하지 않고 테스트와 관련된 컴포넌트들만 스캔한다.

Using this annotation will disable full auto-configuration and instead apply only configuration relevant to JDBC tests. docs.spring.io

그래서 @Repositoy 어노테이션을 붙인 RacingCarDAO는 스캔되지 않는다.

How to solve

  1. @JdbcTest 클래스에 @Import(RacingCarDAO.class) 추가하기 가장 간단한 방법으로 빈 추가가 가능하다. @Import는 주로 Configure 컴포넌트를 불러오는 방법으로 사용된다고 하지만, 위의 예시처럼 한 개의 컴포넌트를 추가하는 것도 가능하다.

  2. Configure 컴포넌트를 만들어서 불러오기 아래와 같이 TestConfiguration 컴포넌트를 만들어 @Import(TestConfiguration.class)를 붙이는 방식으로 사용 가능하다.

@Configuration  
public class TestConfiguration {  
    @Autowired  
    JdbcTemplate jdbcTemplate;  
    @Bean  
    public CarDAO carDAO() {  
        return new RacingCarDAO(this.jdbcTemplate);  
    }  
}

Additional

@JdbcTest는 transactional하고, in-memory database를 사용한다. 즉, 각 테스트가 끝날때마다 roll back 시키고, 실제 데이터베이스에 저장되진 않는다.